#include "seqiot.h"
#include "value.h"
#include "mqtt.h"
#include "scada.h"
#include "aiot_mqtt_api.h"

//iec104数据包格式
static void _scada_payload_init(asdu_dui_104_t *info, mqtt_handler_t *handler, const device_t *device, int typeid) {
    bzero(info, sizeof(asdu_dui_104_t));

    info->rtu = 0;
    info->dui.cot.cause = tc_percyc;
    info->dui.cot.lru = device->super.id;
    info->dui.vsq.num = 0;
    info->dui.vsq.sq = sq_single;
    info->dui.type_id = typeid;
}

//iec104数据包格式
static void _scada_payload_aliot_init(asdu_dui_104_t *info, uint16_t dev_id, int typeid) {
    bzero(info, sizeof(asdu_dui_104_t));

    info->rtu = 0;
    info->dui.cot.cause = tc_percyc;
    info->dui.cot.lru = dev_id;
    info->dui.vsq.num = 0;
    info->dui.vsq.sq = sq_single;
    info->dui.type_id = typeid;
}

static segment_e segments[] = {seg_do, seg_di, seg_ao, seg_ai};

//不带时标的遥信量
static _Bool _apply_digit(const thing_model_t *tag, const value_t *value) {
    return dt_bit == tag->data_type && value->timestamp < TIME_20100101;
}

static uint8_t *_send_digit(uint8_t *ptr, const value_t *value) {
    info_value_q_t *ivq = (info_value_q_t *) ptr;
    ptr += sizeof(info_value_q_t);

    bzero(ivq, sizeof(info_value_q_t));
    ivq->qds.spi = value->bval;

    return ptr;
}

//带时标的遥信量
static _Bool _apply_digit_cp56time2a(const thing_model_t *tag, const value_t *value) {
    return dt_bit == tag->data_type && value->timestamp >= TIME_20100101;
}

static uint8_t *_send_digit_cp56time2a(uint8_t *ptr, const value_t *value) {
    ptr = _send_digit(ptr, value);

    cp56time2a_t *cp56 = (cp56time2a_t *) ptr;
    ptr += sizeof(cp56time2a_t);

    set_cp56time2a(cp56, value->timestamp);

    return ptr;
}

//不带时标的遥测量
static _Bool _apply_analog(const thing_model_t *tag, const value_t *value) {
    return dt_bit != tag->data_type && value->timestamp < TIME_20100101;
}

static uint8_t *_send_analog(uint8_t *ptr, const value_t *value) {
    info_value_i4_t *ivi = (info_value_i4_t *) ptr;
    ptr += sizeof(info_value_i4_t);

    bzero(ivi, sizeof(info_value_i4_t));
    ivi->fval = value->value;

    return ptr;
}

//带时标的遥测量
static _Bool _apply_analog_cp56time2a(const thing_model_t *tag, const value_t *value) {
    return dt_bit != tag->data_type && value->timestamp >= TIME_20100101;
}

static uint8_t *_send_analog_cp56time2a(uint8_t *ptr, const value_t *value) {
    ptr = _send_analog(ptr, value);

    cp56time2a_t *cp56 = (cp56time2a_t *) ptr;
    ptr += sizeof(cp56time2a_t);

    set_cp56time2a(cp56, value->timestamp);

    return ptr;
}

static int _scada_send(mqtt_handler_t *handler, device_t *device, int typeid, int reve,
                       _Bool (*_apply_handle)(const thing_model_t *, const value_t *),
                       uint8_t *(_send_handle)(uint8_t *, const value_t *)) {
    int rc = 0, i, j;
    uint8_t payload[0x100 - 6/*APCI*/];

    asdu_dui_104_t *info = (asdu_dui_104_t *) payload;
    uint8_t *s = payload + sizeof(asdu_dui_104_t);
    uint8_t *e = payload + sizeof(payload) - reve;

    uint8_t *p = s;
    _scada_payload_init(info, handler, device, typeid);
    for (i = 0; i < sizeof(segments) / sizeof(segments[0]); ++i) {
        segment_e segment = segments[i];
        vector_t *tag_list = &device->_product->thing_model[segment];
        vector_t *val_list = &device->thing_value[segment];

        for (j = 0; j < vector_size(val_list); ++j) {
            thing_model_t *tag = vector_get(tag_list, j);
            value_t *value = vector_get(val_list, j);

            if (_apply_handle(tag, value) && value->_changed) {
                value->_changed = false;
                info->dui.vsq.num++;

                info_obj_addr_t *it = (info_obj_addr_t *) p;
                p += sizeof(info_obj_addr_t);
                bzero(it, sizeof(info_obj_addr_t));
                it->tag = thing_model_tag_id(tag);

                p = _send_handle(p, value);

                log_trace("dev_id=%d, tag_id=%d, value=%f", device->super.id, it->tag, value->value);
            }

            if (p > e) {
                rc = mqtt_write(handler, "/user/scada", payload, p - payload);
                if (rc < 0)
                    return rc;

                p = s;
                _scada_payload_init(info, handler, device, typeid);
            }
        }
    }

    if (p > s) {
        rc = mqtt_write(handler, "/user/scada", payload, p - payload);
    }

    return rc;
}

int publish_scada(mqtt_handler_t *handler, device_t *device) {
    int ntag = sizeof(info_obj_addr_t);
    int nivq = sizeof(info_value_q_t);
    int nivi = sizeof(info_value_i4_t);
    int nts = sizeof(cp56time2a_t);

//	_scada_send(handler, device, M_SP_NA_1, ntag + nivq, _apply_digit, _send_digit);
//	_scada_send(handler, device, M_ME_NC_1, ntag + nivi, _apply_analog, _send_analog);
    _scada_send(handler, device, M_SP_TB_1, ntag + nivq + nts, _apply_digit_cp56time2a, _send_digit_cp56time2a);
    _scada_send(handler, device, M_ME_TF_1, ntag + nivi + nts, _apply_analog_cp56time2a, _send_analog_cp56time2a);

    return 0;
}

//带时标的遥测量
static _Bool _apply_aliot_analog_cp56time2a(const uint8_t type, const uint32_t timestamp) {
    return dt_bit != type && timestamp >= TIME_20100101;
}

static uint8_t *_send_aliot_analog_cp56time2a(uint8_t *ptr, const value_t *value) {
    ptr = _send_analog(ptr, value);

    cp56time2a_t *cp56 = (cp56time2a_t *) ptr;
    ptr += sizeof(cp56time2a_t);

    set_cp56time2a(cp56, value->timestamp);

    return ptr;
}

static int _scada_send_aliot(mqtt_handler_t *handler, uint16_t dev_id, uint8_t type, uint32_t timestamp,
                             uint16_t tag_id, float value_send, int typeid, int reve,
                             _Bool (*_apply_handle)(const uint8_t type, const uint32_t timestamp),
                             uint8_t *(_send_handle)(uint8_t *, const value_t *), const char *topic) {
    int rc = 0;
    uint8_t payload[0x100 - 6/*APCI*/];

    asdu_dui_104_t *info = (asdu_dui_104_t *) payload;
    uint8_t *s = payload + sizeof(asdu_dui_104_t);
    uint8_t *e = payload + sizeof(payload) - reve;

    uint8_t *p = s;
    _scada_payload_aliot_init(info, dev_id, typeid);
    //遍历所有thing_model的value
    if (_apply_handle(type, timestamp)) {
        info->dui.vsq.num++;

        info_obj_addr_t *it = (info_obj_addr_t *) p;
        p += sizeof(info_obj_addr_t);
        bzero(it, sizeof(info_obj_addr_t));
        it->tag = tag_id;
        value_t *value = value_new();
        value->timestamp = timestamp;
        value->value = value_send;
        p = _send_handle(p, value);
        log_trace("dev_id=%d, tag_id=%d, value=%f", dev_id, it->tag, value->value);
    }

    if (p > e) {
        rc = aiot_mqtt_pub(handler, topic, payload, p - payload, 1);
        if (rc < 0)
            return rc;
        p = s;
        _scada_payload_aliot_init(info, dev_id, typeid);
    }

    if (p > s) {
        rc = aiot_mqtt_pub(handler, topic, payload, p - payload, 1);
    }

    return rc;
}

int publish_scada_aliot(const float *value, const char *topic, mqtt_handler_t *handler, uint16_t dev_id,
                        block_t *block_send,
                        const uint32_t *timestamp,
                        tag_header_t *tag_send) {
    int ntag = sizeof(info_obj_addr_t);
    int nivi = sizeof(info_value_i4_t);
    int nts = sizeof(cp56time2a_t);
    int rc = 0;
    if (tag_send->is_floating)
        rc = _scada_send_aliot(handler, dev_id, dt_float, *timestamp, tag_send->tag_id,
                               *value, M_ME_TF_1, ntag + nivi + nts, _apply_aliot_analog_cp56time2a,
                               _send_aliot_analog_cp56time2a, topic);

    return rc;
}
